﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace JESAI.Core.Serialize
{
    /// <summary>
    /// 接管c#基元类型转换(bool、char、byte、short/int/long/float/double/decimal
    /// </summary>
    public class PrimitiveConvertor : JsonConverter
    {
        private readonly bool isLongToString;
        private readonly bool isAllToString;
        private readonly int? allNumDigit;
        private readonly int? decimalDigit;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="isAllToString">是否将所有支持的数据类型转换为字符串</param>
        /// <param name="isLongToString">是否将long类型转换为字符串</param>
        /// <param name="allNumDigit">将所有的数字进行四舍五入后保留的小数位数</param>
        /// <param name="decimalDigit">仅针对decimal数据类型进行四舍五入后保留的小数位数</param>
        public PrimitiveConvertor(bool isLongToString = false, bool isAllToString = false, int? allNumDigit = null, int? decimalDigit = null)
        {
            this.isLongToString = isLongToString;
            this.isAllToString = isAllToString;
            this.allNumDigit = allNumDigit;
            this.decimalDigit = decimalDigit;
        }

        /// <summary>
        /// 是否可以转换指定的数据类型
        /// </summary>
        /// <param name="objectType"></param>
        /// <returns></returns>
        public override bool CanConvert(Type objectType)
        {
            var typecode = Type.GetTypeCode(objectType.Name.Equals("Nullable`1") ? objectType.GetGenericArguments().First() : objectType);
            switch (typecode)
            {
                case TypeCode.Boolean:
                case TypeCode.Char:
                case TypeCode.SByte:
                case TypeCode.Byte:
                case TypeCode.Int16:
                case TypeCode.UInt16:
                case TypeCode.Int32:
                case TypeCode.UInt32:
                case TypeCode.Int64:
                case TypeCode.UInt64:
                case TypeCode.Single:
                case TypeCode.Double:
                case TypeCode.Decimal:
                    return true;
                default: return false;
            }
        }

        /// <summary>
        /// 反序列化json
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="objectType"></param>
        /// <param name="existingValue"></param>
        /// <param name="serializer"></param>
        /// <returns></returns>
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            return AsType(reader.Value?.ToString(), objectType);
        }

        /// <summary>
        /// 字符串格式数据转其他类型数据
        /// </summary>
        /// <param name="input">输入的字符串</param>
        /// <param name="destinationType">目标格式</param>
        /// <returns>转换结果</returns>
        public static object AsType(string input, Type destinationType)
        {
            try
            {
                var converter = TypeDescriptor.GetConverter(destinationType);
                if (converter.CanConvertFrom(typeof(string)))
                {
                    return converter.ConvertFrom(null, null, input);
                }

                converter = TypeDescriptor.GetConverter(typeof(string));
                if (converter.CanConvertTo(destinationType))
                {
                    return converter.ConvertTo(null, null, input, destinationType);
                }
            }
            catch
            {
                return null;
            }
            return null;
        }

        /// <summary>
        /// 序列化指定数据类型
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="value"></param>
        /// <param name="serializer"></param>
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var typecode = Type.GetTypeCode(value?.GetType());
            switch (typecode)
            {
                case TypeCode.Boolean:
                case TypeCode.Char:
                case TypeCode.SByte:
                case TypeCode.Byte:
                case TypeCode.Int16:
                case TypeCode.UInt16:
                case TypeCode.Int32:
                case TypeCode.UInt32:
                    {
                        if (isAllToString) writer.WriteValue(value?.ToString());
                        else writer.WriteValue(value);
                        break;
                    }
                case TypeCode.Int64:
                case TypeCode.UInt64:
                    {
                        if (isAllToString || isLongToString) writer.WriteValue(value?.ToString());
                        else
                        {
                            writer.WriteValue(value);
                        }
                        break;
                    }
                case TypeCode.Single:
                    {
                        float val = (float)value;
                        if (allNumDigit != null)
                        {
                            val = float.Parse(val.ToString("F" + allNumDigit));
                        }
                        if (isAllToString) writer.WriteValue(val.ToString());
                        else
                        {
                            writer.WriteValue(val);
                        };
                        break;
                    }
                case TypeCode.Double:
                    {
                        double val = (double)value;
                        if (allNumDigit != null)
                        {
                            val = double.Parse(val.ToString("F" + allNumDigit));
                        }
                        if (isAllToString) writer.WriteValue(val.ToString());
                        else
                        {
                            writer.WriteValue(val);
                        };
                        break;
                    }
                case TypeCode.Decimal:
                    {
                        decimal val = (decimal)value;
                        if (decimalDigit != null)
                        {
                            val = decimal.Parse(val.ToString("F" + decimalDigit));
                        }
                        else if (allNumDigit != null)
                        {
                            val = decimal.Parse(val.ToString("F" + allNumDigit));
                        }
                        if (isAllToString) writer.WriteValue(val.ToString());
                        else
                        {
                            writer.WriteValue(val);
                        };
                        break;
                    }
                default:
                    {
                        writer.WriteValue(value);
                        break;
                    }
            }
        }
    }
}
